/*
 *  This class is part of WorkTimeCalculator Android App
 *  Author: Domenico Pio Novelli
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.dpndeveloper.wtc.dataprovider;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.util.Log;

/**
 * DataBaseHelper: abstract class to extend to provide a Helper to concrete
 * helper to a table of the database
 * 
 * @author: Domenico Pio Novelli
 */
public abstract class BaseDatabaseHelper<T extends Object> extends
		SQLiteOpenHelper {

	public BaseDatabaseHelper(Context context) {
		super(context, DbConstants.DB_NAME, null, 1);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		Log.i("DataProvider", "DataProvider - on Create");

		db.beginTransaction();
		try {
			db.execSQL(DbConstants.TABLE_TIME_INTERVAL_CREATE_STRING);
			db.setTransactionSuccessful();
			Log.i("DataProvider", "DataProvider - on Create - Success");
		} catch (Exception e) {
			Log.e("DataProvider", "DataProvider - on Create - Exception", e);
		} finally {
			db.endTransaction();
		}
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	}

	/**
	 * Method to perform insert into a table
	 * 
	 * @param table
	 *            table to insert to
	 * @param values
	 *            map<column, value> to insert
	 * @return the id of new raw added
	 */
	protected long executeInsertIntoTable(String table,
			Map<String, String> values) {
		SQLiteDatabase db = getWritableDatabase();
		db.beginTransaction();
		try {
			ContentValues contentValues = new ContentValues();
			for (String key : values.keySet()) {
				contentValues.put(key, values.get(key));
			}
			long result = db.insert(table, null, contentValues);
			db.setTransactionSuccessful();
			return result;
		} catch (Exception e) {
			Log.e("DataProvider",
					"DataProvider - executeInsertIntoTable - Exception", e);
			return -1;
		} finally {
			db.endTransaction();
		}
	}

	/**
	 * Method to perform update on a table
	 * 
	 * @param table
	 *            table to update
	 * @param values
	 *            map<column, value> to update
	 * @return true if update execute successfully, false otherwise
	 */
	protected boolean executeUpdateOnTable(String table,
			Map<String, String> values, String id) {
		SQLiteDatabase db = getWritableDatabase();
		db.beginTransaction();
		try {
			ContentValues contentValues = new ContentValues();
			for (String key : values.keySet()) {
				contentValues.put(key, values.get(key));
			}
			boolean result = db.update(table, contentValues, "id=?",
					new String[] { id }) == 1;
			db.setTransactionSuccessful();
			return result;
		} catch (Exception e) {
			Log.e("DataProvider",
					"DataProvider - executeInsertIntoTable - Exception", e);
		} finally {
			db.endTransaction();
		}
		return false;
	}

	/**
	 * Method to perform delete of element in a table
	 * 
	 * @param table
	 *            table of element
	 * @param id
	 *            id of element to delete
	 * @return @return true if delete execute successfully, false otherwise
	 */
	protected boolean executeDeleteOnTable(String table, String id) {
		SQLiteDatabase db = getWritableDatabase();
		db.beginTransaction();
		try {
			boolean result = db.delete(table, "id=?", new String[] { id }) == 1;
			db.setTransactionSuccessful();
			return result;
		} catch (Exception e) {
			Log.e("DataProvider",
					"DataProvider - executeInsertIntoTable - Exception", e);
			return false;
		} finally {
			db.endTransaction();
		}
	}

	/**
	 * Method to execute a generic SQL
	 * 
	 * @param sql
	 *            the SQL query to execut
	 */
	protected void executeSql(String sql) {
		getWritableDatabase().execSQL(sql);
	}

	/**
	 * This method must be implemented to extract the data of a row in a Model
	 * Object
	 * 
	 * @param cursor
	 *            Cursor to extract a row
	 * @return Data in the Row
	 */
	protected abstract T extractResult(Cursor cursor);

	/**
	 * This method executes select on db It uses extractResult(Cursor cursor) to
	 * extract a row
	 * 
	 * @return List of Rows in Object Model
	 */
	protected List<T> executeSelect(String table, String[] columns,
			String selection, String[] selectionArgs, String orderBy) {
		List<T> results = new ArrayList<T>();
		try {
			SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
			builder.setTables(table);
			Cursor cursor = getReadableDatabase().query(table, columns,
					selection, selectionArgs, null, null, orderBy);
			while (cursor.moveToNext())
				results.add(extractResult(cursor));
			cursor.close();
			return results;
		} catch (Exception e) {
			Log.e("DataProvider", "DataProvider - Exception", e);
			return null;
		} finally {
		}
	}

	/**
	 * <p>
	 * Method to implement to perform selection of elements of a type T by the
	 * given query and parameters
	 * <p>
	 * <p>
	 * Example of query:
	 * <ul>
	 * <li>sql: select * from tableX where y=? and z=?</li>
	 * <li>selectionArgs content: "valueY", "valueZ"</li>
	 * </ul>
	 * 
	 * @param sql
	 *            the SQL query
	 * @param selectionArgs
	 *            the argument
	 * @return
	 */
	protected List<T> executeSelect(String sql, String[] selectionArgs) {
		List<T> results = new ArrayList<T>();
		Cursor cursor = null;
		try {
			cursor = getReadableDatabase().rawQuery(sql, selectionArgs);
			while (cursor.moveToNext())
				results.add(extractResult(cursor));
			return results;
		} catch (Exception e) {
			Log.e("DataProvider", "DataProvider - Exception", e);
			return null;
		} finally {
			cursor.close();
		}
	}

	/**
	 * Method to implement to perform insert of the given element
	 * 
	 * @param element
	 * @return
	 */
	public abstract long insert(T element);

	/**
	 * Method to implement to perform update of the given element
	 * 
	 * @param element
	 * @return
	 */
	public abstract boolean update(T element);

	/**
	 * Method to implement to perform delete of the given element
	 * 
	 * @param element
	 * @return
	 */
	public abstract boolean delete(T element);

	/**
	 * Method to implement to perform selection of all elements of a type T
	 * 
	 * @return
	 */
	public abstract List<T> selectAll();

	/**
	 * Method to implement to perform selection of element with the given Id
	 * 
	 * @param id
	 * @return
	 */
	public abstract T select(Integer id);

	/**
	 * Method to implement to perform selection of elements of a type T by the
	 * given query and parameters
	 * 
	 * @param selection
	 * @param params
	 * @return
	 */
	public abstract List<T> selectByParams(String selection, String[] params);
}
